﻿// Decompiled with JetBrains decompiler
// Type: Netick.NetworkDictionary`2
// Assembly: Netick, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 6726ECA1-C773-4CF7-8952-E81B30D93B52
// Assembly location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.dll
// XML documentation location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.xml

using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;

#nullable disable
namespace Netick;

/// <summary>
/// A networked <see cref="T:System.Collections.Generic.Dictionary`2" /> collection.
/// </summary>
public sealed class NetworkDictionary<TKey, TValue> : 
  NetworkDictionary,
  INetworkCollection,
  IEnumerable<KeyValuePair<TKey, TValue>>,
  IEnumerable
  where TKey : unmanaged
  where TValue : unmanaged
{
  internal int _offsetToIterationList;
  private Dictionary<TKey, TValue> _original;

  public NetworkDictionary(int capacity)
  {
    this._length = capacity;
    this._lengthDoubled = capacity * 2;
    this._original = new Dictionary<TKey, TValue>(capacity);
  }

  public override unsafe void InternalInit(
    INetickNetworkScript beh,
    int* state,
    int elementSizeWords,
    int flags)
  {
    base.InternalInit(beh, state, elementSizeWords, flags);
    this._entrySizeWords = NetickUtils.GetWordSize(sizeof (NetworkDictionary<TKey, TValue>.Entry));
    this._offsetToIterationList = 1 + this._lengthDoubled * this._entrySizeWords;
  }

  public override unsafe void InternalReset()
  {
    *this._state = 0;
    for (int index = 0; index < this._length; ++index)
      *this.GetIterationListElementPointer(index) = 0;
    for (int index = 0; index < this._lengthDoubled; ++index)
    {
      this.GetEntryPtr(index)->IterationIndex = -1;
      this.GetEntryPtr(index)->Key = default (TKey);
      this.GetEntryPtr(index)->Value = default (TValue);
    }
    foreach (KeyValuePair<TKey, TValue> keyValuePair in this._original)
      this.Add(keyValuePair.Key, keyValuePair.Value);
    this.Count = this._original.Count;
  }

  /// <summary>Removes all keys and values from the dictionary.</summary>
  public void Clear()
  {
    for (int index = 0; index < this._lengthDoubled; ++index)
      this.SetEntryData(index, new NetworkDictionary<TKey, TValue>.Entry(default (TKey), default (TValue), -1, false), false);
    this.Count = 0;
  }

  /// <summary>
  /// Gets or sets the value associated with the specified key.
  /// </summary>
  /// <param name="key">The key whose value to get or set.</param>
  /// <returns>The value associated with the specified key.</returns>
  /// <exception cref="T:System.Collections.Generic.KeyNotFoundException">Thrown when the key does not exist in the dictionary.</exception>
  public TValue this[TKey key]
  {
    get
    {
      TValue obj;
      if (this.TryGetValue(key, out obj))
        return obj;
      throw new KeyNotFoundException();
    }
    set
    {
      if (this.SetKeyValue(key, value))
        return;
      this.Add(key, value);
    }
  }

  /// <summary>
  /// Gets the number of elements contained in the dictionary.
  /// </summary>
  public unsafe int Count
  {
    get => (IntPtr) this._state == IntPtr.Zero ? this._original.Count : *this._state;
    internal set
    {
      this._entity.SetState_Collection(&value, this._state, 1, (this._flags & NetworkCollectionMeta.HasOnChanged) == NetworkCollectionMeta.HasOnChanged ? 1 : 0, (this._flags & NetworkCollectionMeta.IsInputSourceOnly) == NetworkCollectionMeta.IsInputSourceOnly ? 1 : 0, (this._flags & NetworkCollectionMeta.HasOnChanged) == NetworkCollectionMeta.HasOnChanged ? 1 : 0);
    }
  }

  private unsafe NetworkDictionary<TKey, TValue>.Entry* GetEntryPtr(int index)
  {
    return (NetworkDictionary<TKey, TValue>.Entry*) (this._state + 1 + index * this._entrySizeWords);
  }

  private unsafe NetworkDictionary<TKey, TValue>.Entry GetEntryData(int index)
  {
    return *(NetworkDictionary<TKey, TValue>.Entry*) (this._state + 1 + index * this._entrySizeWords);
  }

  private unsafe int* GetIterationListElementPointer(int index)
  {
    return this._state + this._offsetToIterationList + index;
  }

  private unsafe int GetIterationListElementData(int index)
  {
    return (this._state + this._offsetToIterationList)[index];
  }

  internal unsafe void SetEntryData(
    int index,
    NetworkDictionary<TKey, TValue>.Entry value,
    bool invokeOnChanged)
  {
    this._entity.SetState_Collection((int*) &value, this._state + 1 + index * this._entrySizeWords, this._entrySizeWords, (this._flags & NetworkCollectionMeta.HasOnChanged) == NetworkCollectionMeta.HasOnChanged & invokeOnChanged ? 1 : 0, (this._flags & NetworkCollectionMeta.IsInputSourceOnly) == NetworkCollectionMeta.IsInputSourceOnly ? 1 : 0, (this._flags & NetworkCollectionMeta.HasOnChanged) == NetworkCollectionMeta.HasOnChanged ? 1 : 0);
  }

  internal unsafe void SetIterationListElement(int index, int value)
  {
    this._entity.SetState_Collection(&value, this._state + this._offsetToIterationList + index, 1, 0, (this._flags & NetworkCollectionMeta.IsInputSourceOnly) == NetworkCollectionMeta.IsInputSourceOnly ? 1 : 0, (this._flags & NetworkCollectionMeta.HasOnChanged) == NetworkCollectionMeta.HasOnChanged ? 1 : 0);
  }

  /// <summary>Adds the specified key and value to the dictionary.</summary>
  /// <param name="key">The key of the element to add.</param>
  /// <param name="value">The value of the element to add.</param>
  /// <returns>true if the key/value pair was added; false if the key already exists or the dictionary is full.</returns>
  public unsafe bool Add(TKey key, TValue value)
  {
    if ((IntPtr) this._state == IntPtr.Zero)
    {
      this._original.Add(key, value);
      return true;
    }
    if (this.Count >= this._length)
      return false;
    int index1 = (key.GetHashCode() & int.MaxValue) % this._lengthDoubled;
    int num = index1;
    int index2 = -1;
    do
    {
      NetworkDictionary<TKey, TValue>.Entry entryData = this.GetEntryData(index1);
      if (index2 == -1)
      {
        if (entryData.IsEmpty)
          index2 = index1;
        else if (EqualityComparer<TKey>.Default.Equals(entryData.Key, key))
          return false;
        index1 = (index1 + 1) % this._lengthDoubled;
      }
      else
        break;
    }
    while (index1 != num);
    if (index2 == -1)
      return false;
    this.SetEntryData(index2, new NetworkDictionary<TKey, TValue>.Entry(key, value, this.Count, true), false);
    this.SetIterationListElement(this.Count, index2);
    ++this.Count;
    return true;
  }

  /// <summary>
  /// Removes the value with the specified key from the dictionary.
  /// </summary>
  /// <param name="key">The key of the element to remove.</param>
  /// <returns>true if the element is successfully removed; otherwise, false.</returns>
  public bool Remove(TKey key)
  {
    NetworkDictionary<TKey, TValue>.Entry entry1;
    int entry2 = this.FindEntry(key, out entry1);
    if (entry2 == -1)
      return false;
    int iterationIndex = entry1.IterationIndex;
    int iterationListElementData = this.GetIterationListElementData(this.Count - 1);
    NetworkDictionary<TKey, TValue>.Entry entryData1 = this.GetEntryData(iterationListElementData) with
    {
      IterationIndex = iterationIndex
    };
    this.SetEntryData(iterationListElementData, entryData1, false);
    this.SetIterationListElement(iterationIndex, this.GetIterationListElementData(this.Count - 1));
    NetworkDictionary<TKey, TValue>.Entry entryData2 = this.GetEntryData(entry2) with
    {
      IterationIndex = -1
    };
    this.SetEntryData(entry2, entryData2, false);
    --this.Count;
    return true;
  }

  /// <summary>
  /// Tries to get the value associated with the specified key.
  /// </summary>
  /// <param name="key">The key whose value to get.</param>
  /// <param name="value">When this method returns, contains the value associated with the specified key, if the key is found; otherwise, the default value for the type of the value parameter.</param>
  /// <returns>true if the key was found; otherwise, false.</returns>
  public bool TryGetValue(TKey key, out TValue value)
  {
    NetworkDictionary<TKey, TValue>.Entry entry;
    if (this.FindEntry(key, out entry) == -1)
    {
      value = default (TValue);
      return false;
    }
    value = entry.Value;
    return true;
  }

  /// <summary>
  /// Sets the value associated with the specified key if it exists in the dictionary.
  /// If the key is found, updates its value.
  /// </summary>
  /// <returns>true if the key was found and updated; otherwise, false</returns>
  public bool SetKeyValue(TKey key, TValue value)
  {
    NetworkDictionary<TKey, TValue>.Entry entry1;
    int entry2 = this.FindEntry(key, out entry1);
    if (entry2 == -1)
      return false;
    entry1.Value = value;
    this.SetEntryData(entry2, entry1, true);
    return true;
  }

  private int FindEntry(TKey key, out NetworkDictionary<TKey, TValue>.Entry value)
  {
    int index = (key.GetHashCode() & int.MaxValue) % this._lengthDoubled;
    int num = index;
    do
    {
      NetworkDictionary<TKey, TValue>.Entry entryData = this.GetEntryData(index);
      if (entryData.IsEverUsed == 0)
      {
        value = new NetworkDictionary<TKey, TValue>.Entry();
        return -1;
      }
      if (!entryData.IsEmpty && EqualityComparer<TKey>.Default.Equals(entryData.Key, key))
      {
        value = entryData;
        return index;
      }
      index = (index + 1) % this._lengthDoubled;
    }
    while (index != num);
    value = new NetworkDictionary<TKey, TValue>.Entry();
    return -1;
  }

  /// <summary>
  /// Determines whether the dictionary contains the specified key.
  /// </summary>
  /// <param name="key">The key to locate in the dictionary.</param>
  /// <returns>true if the dictionary contains an element with the specified key; otherwise, false.</returns>
  public bool ContainsKey(TKey key)
  {
    return this.FindEntry(key, out NetworkDictionary<TKey, TValue>.Entry _) != -1;
  }

  /// <summary>
  /// Determines whether the dictionary contains a specific value.
  /// </summary>
  /// <param name="value">The value to locate in the dictionary.</param>
  /// <returns>true if the value is found; otherwise, false.</returns>
  public bool ContainsValue(TValue value)
  {
    for (int index = 0; index < this.Count; ++index)
    {
      if (this.GetEntryData(this.GetIterationListElementData(index)).Value.Equals((object) value))
        return true;
    }
    return false;
  }

  public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
  {
    for (int i = 0; i < this.Count; ++i)
    {
      NetworkDictionary<TKey, TValue>.Entry entryData = this.GetEntryData(this.GetIterationListElementData(i));
      yield return new KeyValuePair<TKey, TValue>(entryData.Key, entryData.Value);
    }
  }

  IEnumerator IEnumerable.GetEnumerator() => (IEnumerator) this.GetEnumerator();

  [StructLayout(LayoutKind.Sequential, Pack = 4)]
  internal struct Entry(TKey key, TValue value, int iterationIndex, bool isEverUsed)
  {
    public int IterationIndex = iterationIndex;
    public int IsEverUsed = isEverUsed ? 1 : 0;
    public TKey Key = key;
    public TValue Value = value;

    public bool IsEmpty => this.IterationIndex == -1;
  }
}
